package com.lingtianyu.tech.erbaoshen.core.provider;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lingtianyu.tech.erbaoshen.core.context.ErBaoShenRpcContext;
import com.lingtianyu.tech.erbaoshen.core.context.ErBaoShenRpcResultContext;
import com.lingtianyu.tech.erbaoshen.core.provider.bean.ErBaoShenRpcServiceBeans;
import com.lingtianyu.tech.erbaoshen.core.provider.bean.ServiceBean;
import com.lingtianyu.tech.erbaoshen.core.provider.model.ErBaoShenRpcReq;
import com.lingtianyu.tech.erbaoshen.core.provider.model.ErBaoShenRpcResp;
import com.lingtianyu.tech.erbaoshen.core.util.ErBaoShenFinals;
import com.lingtianyu.tech.erbaoshen.core.util.KeyUtil;
import com.lingtianyu.tech.erbaoshen.core.util.SerializeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;

@RestController
@Slf4j
public class ErBaoShenRpcController {
    @Resource(name = "erBaoShenRpcServiceBeans")
    private ErBaoShenRpcServiceBeans erBaoShenRpcServiceBeans;
    @Resource
    private SerializeUtil serializeUtil;
    private static final ObjectMapper objectMapper = new ObjectMapper();

    @PostMapping("/erbaoshen/rpc/service/{className}/{methodName}")
    public ResponseEntity<String> runRpcService(@PathVariable("className") String className, @PathVariable("methodName") String methodName,
                                                @RequestBody String erBaoShenRpcReqString) {
        try {
            ErBaoShenRpcReq erBaoShenRpcReq = objectMapper.readValue(erBaoShenRpcReqString, ErBaoShenRpcReq.class);
            String group = erBaoShenRpcReq.getGroup() != null ? erBaoShenRpcReq.getGroup() : "";
            String version = erBaoShenRpcReq.getVersion() != null ? erBaoShenRpcReq.getVersion() : "";
            String key = KeyUtil.getKey(className, group, version);
            ServiceBean serviceBean = erBaoShenRpcServiceBeans.getServiceBeanMap().get(key);
            if (serviceBean == null) {
                throw new NullPointerException("service " + key + " not found ");
            }
            Object ref = serviceBean.getRef();
            ErBaoShenRpcResultContext.clear();
            ErBaoShenRpcContext.clear();
            ErBaoShenRpcContext.setAttachments(erBaoShenRpcReq.getContext());
            List<String> paramTypes = erBaoShenRpcReq.getParamTypes();
            Method method = null;
            if (paramTypes == null || paramTypes.isEmpty()) {
                method = serviceBean.getInterfaceClass().getMethod(methodName);
                Object invoke = method.invoke(ref);
                return ResponseEntity.status(HttpStatus.OK).header(ErBaoShenFinals.SERIALIZE_TYPE_HEADER, erBaoShenRpcReq.getSerializeType()).body(serializeUtil.toSerializeParams(invoke, erBaoShenRpcReq.getSerializeType()));
            } else {
                List<Object> params = erBaoShenRpcReq.getParams();
                Class<?>[] classes = new Class<?>[paramTypes.size()];
                for (int i = 0; i < paramTypes.size(); i++) {
                    String paramType = paramTypes.get(i);
                    if ("boolean".equals(paramType)) {
                        classes[i] = boolean.class;
                        continue;
                    } else if ("char".equals(paramType)) {
                        classes[i] = char.class;
                        continue;
                    } else if ("byte".equals(paramType)) {
                        classes[i] = byte.class;
                        continue;
                    } else if ("short".equals(paramType)) {
                        classes[i] = short.class;
                        continue;
                    } else if ("int".equals(paramType)) {
                        classes[i] = int.class;
                        continue;
                    } else if ("long".equals(paramType)) {
                        classes[i] = long.class;
                        continue;
                    } else if ("float".equals(paramType)) {
                        classes[i] = float.class;
                        continue;
                    } else if ("double".equals(paramType)) {
                        classes[i] = double.class;
                        continue;
                    }
                    Class<?> aClass = Class.forName(paramType);
                    classes[i] = aClass;
                }

                method = serviceBean.getInterfaceClass().getMethod(methodName, classes);
                //TODO 序列化后，如果参数是Map<String,Object> 这种，那value的类型会不准确，和原始类型有出入
                Object[] paramArray = new Object[paramTypes.size()];
                Type[] genericParameterTypes = method.getGenericParameterTypes();
                for (int i = 0; i < genericParameterTypes.length; i++) {
                    paramArray[i] = serializeUtil.unSerializeParams(params.get(i), genericParameterTypes[i], erBaoShenRpcReq.getSerializeType());
                }
                Object invoke = method.invoke(ref, paramArray);
                ErBaoShenRpcResultContext.clear();
                ErBaoShenRpcContext.clear();
                return ResponseEntity.status(HttpStatus.OK).header(ErBaoShenFinals.SERIALIZE_TYPE_HEADER, erBaoShenRpcReq.getSerializeType()).body(serializeUtil.toSerializeParams(invoke, erBaoShenRpcReq.getSerializeType()));
            }
        } catch (Throwable e) {
            log.error(e.getMessage(), e);
            ErBaoShenRpcResp erBaoShenRpcResp = new ErBaoShenRpcResp();
            erBaoShenRpcResp.setSuccess(false);
            erBaoShenRpcResp.setErrorCode("400");
            erBaoShenRpcResp.setErrorMessage(e.getClass() + " " + e.getMessage());
            try {
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(objectMapper.writeValueAsString(erBaoShenRpcResp));
            } catch (JsonProcessingException ex) {
                throw new RuntimeException(ex);
            }
        }
    }


}
